理解Scala语言中Null/Nothing/Nil/None/Unit的区别 您所在的位置:网站首页 cost nothing和cost none 理解Scala语言中Null/Nothing/Nil/None/Unit的区别

理解Scala语言中Null/Nothing/Nil/None/Unit的区别

2024-07-07 22:35| 来源: 网络整理| 查看: 265

Prologue

Scala语言虽然是构建在JVM体系之上的,但为了适应函数式编程的需要,它的语法和Java几乎完全不同,在很多基础层面——比如类型系统——也是自成一派的。在Scala类型系统中,Null、Nothing、Nil、None、Unit这些类型看起来似乎都表达“空”的语义,但实际上很有一些区别,容易混淆。本文来简单分辨一下。

作为预热,可以先看看来自官方文档的class hierarchy diagram。

https://www.scala-lang.org/files/archive/spec/2.12/12-the-scala-standard-library.html Null

Scala中所有类型的父类型是Any,Any有两个一级子类型:AnyVal和AnyRef,分别对应所有值类型(primitives)和引用类型(references)的根。Null是所有AnyRef的子类,处于Scala类型体系的次底层。并且它只有一个实例,就是null,表示空引用。

在Scala源代码中,Null作为一个空的特征(trait)存在。

package scala /** `Null` is - together with [[scala.Nothing]] - at the bottom of the Scala type hierarchy. * * `Null` is the type of the `null` literal. It is a subtype of every type * except those of value classes. Value classes are subclasses of [[AnyVal]], which includes * primitive types such as [[Int]], [[Boolean]], and user-defined value classes. * * Since `Null` is not a subtype of value types, `null` is not a member of any such type. * For instance, it is not possible to assign `null` to a variable of type [[scala.Int]]. */ sealed trait Null

可见,null只能赋值给引用类型,不能赋值给值类型。而在运行时,Null特征会以抽象类Null$的形式存在于JVM中。

package scala package runtime /** * Dummy class which exist only to satisfy the JVM. It corresponds to * `scala.Null`. If such type appears in method signatures, it is erased * to this one. A private constructor ensures that Java code can't create * subclasses. The only value of type Null$ should be null */ sealed abstract class Null$ private ()

这样就阻断了new Null()以及继承Null的可能性,维护了空引用的唯一性。下面举个例子说明Null和null的意义。

scala> var myStr: String = _ myStr: String = null scala> var myNull: Null = null myNull: Null = null scala> myNull = new Null :12: error: class Null is abstract; cannot be instantiated myNull = new Null ^ scala> def useNull(n: Null) = { println("Hello null!") } useNull: (n: Null)Unit scala> useNull(myStr) :14: error: type mismatch; found : String required: Null useNull(myStr) ^ scala> useNull(myNull) Hello null! Nothing

Nothing在Null的基础上更进一步(说是更退一步也可以)。它是所有Any(包含Null在内)的子类,处于Scala类型体系的最底层,表示“no instance”,即真正意义上的“没有任何实例”。其定义与Null是相同的。

package scala /** `Nothing` is - together with [[scala.Null]] - at the bottom of Scala's type hierarchy. * * `Nothing` is a subtype of every other type (including [[scala.Null]]); there exist * ''no instances'' of this type. Although type `Nothing` is uninhabited, it is * nevertheless useful in several ways. For instance, the Scala library defines a value * [[scala.collection.immutable.Nil]] of type `List[Nothing]`. Because lists are covariant in Scala, * this makes [[scala.collection.immutable.Nil]] an instance of `List[T]`, for any element of type `T`. * * Another usage for Nothing is the return type for methods which never return normally. * One example is method error in [[scala.sys]], which always throws an exception. */ sealed trait Nothing

在运行时,Nothing特征会以抽象类Nothing$的形式存在于JVM中。特别注意,Nothing可以作为异常类型被抛出,后面会见到它的这种用法。

package scala package runtime /** * Dummy class which exist only to satisfy the JVM. It corresponds * to `scala.Nothing`. If such type appears in method * signatures, it is erased to this one. */ sealed abstract class Nothing$ extends Throwable

由上文的叙述可知,Nothing类型的对象永远无法被赋值,但是它的灵活性也是最大的,可以用来作为任意类型的marker interface。来看下面的例子即可理解。

scala> val myStrList: List[String] = List[Nothing]() myStrList: List[String] = List() scala> val myIntList: List[Int] = List[Nothing]() myIntList: List[Int] = List() scala> val myStrList2: List[String] = List[Nothing]("123", "456") :11: error: type mismatch; found : String("123") required: Nothing val myStrList2: List[String] = List[Nothing]("123", "456") ^ :11: error: type mismatch; found : String("456") required: Nothing val myStrList2: List[String] = List[Nothing]("123", "456") ^ Nil

直接来看Nil的定义,它位于scala.collection.immutable包下。

case object Nil extends List[Nothing] { override def isEmpty = true override def head: Nothing = throw new NoSuchElementException("head of empty list") override def tail: List[Nothing] = throw new UnsupportedOperationException("tail of empty list") // Removal of equals method here might lead to an infinite recursion similar to IntMap.equals. override def equals(that: Any) = that match { case that1: scala.collection.GenSeq[_] => that1.isEmpty case _ => false } }

可见,Nil就是一个空的List[Nothing],即一个可以封装任何类型元素但又没有元素的容器。由于它确定是空的,所以其head()和tail()方法都应抛出异常,这里就利用了上述Nothing可以作为异常类型的特性。从某种意义上讲,方法抛出异常表示没有按照既定的返回值类型进行返回,所以Nothing的语义正合适。

None

在说None之前,应该先来了解Option。

Scala中的Option与Java 8引入的java.util.Optional语义相同,表示“一个实例有可能不为空,也有可能为空”,是OOP大一统思想下的产物,旨在通过让用户提前判空来避免讨厌的NullPointerException,降低直接返回null的风险。在Scala类Option[+A]的伴生对象中,apply()方法的定义如下。

/** An Option factory which creates Some(x) if the argument is not null, * and None if it is null. * * @param x the value * @return Some(value) if value != null, None if value == null */ def apply[A](x: A): Option[A] = if (x == null) None else Some(x)

当实例x不为null时,返回Some(x),否则返回的就是None了。Some和None都是Optional的子类,定义如下。

@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4 final case class Some[+A](x: A) extends Option[A] { def isEmpty = false def get = x } @SerialVersionUID(5066590221178148012L) // value computed by serialver for 2.11.2, annotation added in 2.11.4 case object None extends Option[Nothing] { def isEmpty = true def get = throw new NoSuchElementException("None.get") }

可见,None的本质就是Option[Nothing]。还是举个例子吧。

scala> def getPositive(num: Int): Option[Int] = { | if (num > 0) Some(num) | else None | } getPositive: (num: Int)Option[Int] scala> def showPositive(num: Int) = { | getPositive(num) match { | case Some(x) => println("Got a positive number") | case None => println("Got a non-positive number") | } | } showPositive: (num: Int)Unit scala> showPositive(7) Got a positive number scala> showPositive(-7) Got a non-positive number

Scala可以优雅地使用模式匹配来处理Some和None的情况,所以Option在主要以Scala写成的开源框架(如Spark)中应用甚为广泛。

Unit

Unit在上面的例子中已经出现过多次了,它完全等同于Java里的void,表示函数或方法没有返回值。它的定义如下。

package scala /** `Unit` is a subtype of [[scala.AnyVal]]. There is only one value of type * `Unit`, `()`, and it is not represented by any object in the underlying * runtime system. A method with return type `Unit` is analogous to a Java * method which is declared `void`. */ final abstract class Unit private extends AnyVal { override def getClass(): Class[Unit] = null }

可见,Unit是一种特殊的值类型(AnyVal)的子类,并且它不代表任何实际的值类型,故其getClass()方法返回null。在Unit伴生对象的拆装箱逻辑中,我们可以发现:

/** Transform a value type into a boxed reference type. * * @param x the Unit to be boxed * @return a scala.runtime.BoxedUnit offering `x` as its underlying value. */ def box(x: Unit): scala.runtime.BoxedUnit = scala.runtime.BoxedUnit.UNIT

在BoxedUnit的定义里,也能够发现Unit等同于void的蛛丝马迹。

public final static Class TYPE = java.lang.Void.TYPE; The End

民那晚安晚安。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有